# -*- coding: utf-8 -*-

""" Para no tener que buscar los tipos de property para el tipo
UserProperty lo que uso es el snippet sacado de aca:
http://www.djangosnippets.org/snippets/1034/
"""

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.db.models.query import QuerySet
from django import forms

from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User


# TODO validar que la key de la property no sea alguno de estos
# valores
INVALID_KEYS = (
        'mail',
        'first_name',
        'last_name',
        'password',
)


OPTION_TYPES = (
        ('RDB', _('Radio Button')),
        ('CHK', _('Check Box')),
        ('CMB', _('Combos/Select'))
)



class SubclassingQuerySet(QuerySet):
    def __getitem__(self, k):
        result = super(SubclassingQuerySet, self).__getitem__(k)
        if isinstance(result, models.Model) :
            return result.as_leaf_class()
        else :
            return result
    def __iter__(self):
        for item in super(SubclassingQuerySet, self).__iter__():
            yield item.as_leaf_class()


class UserPropertyManager(models.Manager):

    def get_query_set(self):
        return SubclassingQuerySet(self.model)        

class UserProperty(models.Model):
    """ Representa una property que se le va a pedir al usuario.
    
    Una property es un campo que va a aparecer cuando el usuario se
    este creando y que puede llegar a ser utilizada despues.

    Es importante tener en cuenta que la key tiene que ser unica entre
    todos los tipos de properties. Por ahora no estoy seguro de que
    sea realmente necesario pero estoy seguro de que simplifica los problemas.
    """
    key = models.CharField(max_length=100, unique=True, blank=False, \
                            null=False)
    description = models.CharField(max_length=100, blank=False, \
                            null=False, help_text= \
                            _("La clave de i18n que tiene que aperecer"\
                              "en el form"))
    required = models.BooleanField(default=False)
    order = models.IntegerField(unique=True)
    content_type = models.ForeignKey(ContentType,editable=False,null=True)
    objects = UserPropertyManager()

    class Meta:
        ordering = ['order']

    def get_form_field(self):
        """ Devuelve el field que se tiene que usar para representar
        esta property en un form.

        El resultado de esto es lo que le aparece al usuario cuando
        se da de alta o modicia sus datos.
        """
        return None

    def save(self, *args, **kwargs):
        if (not self.content_type):
            self.content_type = \
                ContentType.objects.get_for_model(self.__class__)
            super(UserProperty, self).save(*args, **kwargs)  

    def as_leaf_class(self):
        content_type = self.content_type
        model = content_type.model_class()
        if (model == UserProperty):
            return self
        return model.objects.get(id=self.id)
    

    def set_common_values(self, field):
        """ Set the common values (description, required) to the field.

        :parameters:
            field: `django.forms.Field`
                the field of to be returned by the `get_form_field`.

        :returns:
            the field with its description and if it is required.
        """
        field.required = self.required
        field.description = self.description
        return field 

    def __unicode__(self):
        return self.key

class OptionsProperty(UserProperty):
    """ En este tipo de opciones el usuario tiene varios valores de los
    que puede elegir.

    Basicamente es cuando te aparece lo siguiente:
        - combo
        - radiobutton
        - checkboxes

    El valor ``puede_no_seleccionar`` indica si el usuario esta obligado
    a seleccionar una de las opciones o si puede no elegir alguna.
    """
    # TODO falta modificiarlo para que pueda seleccionar varios al mismo
    # tiempo.
    choices_type = models.CharField(choices=OPTION_TYPES, max_length=3, \
                            null=False, blank=False)
    pude_no_seleccionar = models.BooleanField(default=False)    
    objects = UserPropertyManager()

    def get_form_field(self):
        """ Devuelve un Select con todos los posibles valores.
        """
        choices = [(option.value, option.description) for \
                  option in self.optionvalue_set.all()]
        if self.choices_type == 'RDB':
            widget = forms.RadioSelect
            field = forms.ChoiceField(choices, widget=widget)
        elif self.choices_type == 'CHK':
            widget=forms.CheckboxSelectMultiple
            field = forms.MultipleChoiceField(choices, widget=widget)
        else:           
            widget = None
            field = forms.ChoiceField(choices)

        return self.set_common_values(field)


    def __unicode__(self):
        return self.key

class OptionValue(models.Model):
    """ Representa uno de los valor que puede llegar a seleccionar el usuario
    en las propiedades de opciones.

    Por ejemplo, es una de las opciones del combo o uno de los radio buttons.
    En este caso la descripcion es lo que se le muestra al usuario por 
    pantalla mientras que el valor es lo que se graba en la base de datos.
    """
    description = models.CharField(max_length=200, null=False, blank=False, \
                                   help_text = _(\
                                        "El texto que le aparece al usuario."))
    value = models.CharField(max_length=100, null=False, blank=False)
    property = models.ForeignKey(OptionsProperty)
    

    def __unicode__(self):
        return self.property.key + "-" + self.value

class FileProperty(UserProperty):
    """ Este tipo de property es para que el usuario pueda subir archivos.
    Por ejemplo, para que el usuario pueda subir su CV o cosas similares.
    
    """
    upload_path = models.CharField(max_length=200, null=False, blank=False, \
         help_text=_("A donde va ir a parar el archivo en el servidor."))
    document_type = models.CharField(help_text= _("Para que el usuario"\
                                " solo pueda subir ese tipo de archivos."),
                                max_length=200)
    max_size = models.IntegerField(help_text=_("El maximo tamanio del "\
                                "archivo"))
    objects = UserPropertyManager()

    def get_form_field(self):
        """ Devuelve un upload field.
        """
        return self.set_common_values(forms.FileField())

class BooleanProperty(UserProperty):
    """ Propiedad para que el usuario seleccione un valor del tipo True/False
    (Si/No).
    """

    objects = UserPropertyManager()

    def get_form_field(self):
        """ Devuelve un checkbox. 
        """
        return self.set_common_values(forms.BooleanField())


class IntegerProperty(UserProperty):
    """ Propiedad a la que se la asocia con un valor.
    """
    max_digits = models.IntegerField(help_text=_(\
            "Cantidad de digitos en la parte entera"))
    decimal_places = models.IntegerField(help_text=_(\
            "Cantidad de digitos en la parte decimal"))
    max_value = models.IntegerField(help_text=_(\
            "Maximo valor que puede llegar a tomar"), null=True, blank=True)
    min_value = models.IntegerField(help_text=_(\
            "Minimo valor que puede llegar a tomar"), null=True, blank=True)


    objects = UserPropertyManager()

    def get_form_field(self):
        field = forms.DecimalField(self.max_value, self.min_value, \
                    self.max_digits, self.decimal_places)                   
        return self.set_common_values(field)


class TextProperty(UserProperty):
    """ Propiedad que se la asocia con un texto.
    """
    is_textarea = models.BooleanField(help_text=_(\
            "Indica si al usuario se le muestra un text area o un input"))
    max_length = models.IntegerField(help_text=_(\
            "Si es un input entonces el max_length del mismo."))


    objects = UserPropertyManager()

    def get_form_field(self):       
        if self.is_textarea:
            field = forms.TextInput()
        else:
            field = forms.CharField()
        return self.set_common_values(field)


class UserPropertyValue(models.Model):
    """ Relaciona el valor de una propiedad con un usuario.
    """
    property = models.ForeignKey(UserProperty)
    user = models.ForeignKey(User)
    value = models.TextField()

    class Meta:
        ordering = ['property']

    def __unicode__(self):
        return "%s-%s" % (self.property, self.user)
